/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algoritmo.puzzle;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Luis Alberto GonzÃ¡lez Pineda//
 * Creative Comons
 * Derechos Reservados
 */
public class Puzzle {

//    Crear una matriz de 3x4 de tipo entero
    private int[][] matriz = new int[3][4];
//    crear una lista de enteros
    private List<Integer> nums = new ArrayList();
//    crear una variable de tipo booleano
    private boolean correcto;
    private List<Integer> errores = new ArrayList<Integer>();
    private int error = 0;
//    -----------------------------------------------------------
/**Este metodo permirte hallar la respuesta optima al problema
* cumpliendo con las condiciones establecidas,
* recibe un entero, que es el numero de matrices
* que se generaran, ademas en este metodo se
* calculara el tiempo que se demora en nanosegundos
* escribe el numero de la matriz y los errores
  */
    public void hallarOptimo(int numMatrices) {
        for (int i = 0; i < numMatrices; i++) {
            long tiempoInicio = System.nanoTime();
            generarNumeros();
            llenarMatriz();
            System.out.println("Matriz # " + (i + 1));
            imprimirMatriz();
            recorrerMatriz();
            llenarListaErrores();
            System.out.println("\nErrores: " + (error));
            getNums().clear();
            error = 0;
            long totalTiempo = System.nanoTime() - tiempoInicio;
            System.out.println("El tiempo de demora es: " + totalTiempo + " nanoseg");
            System.out.println();
        }
        calcularOptimo();
    }
//    ----------------------------------------------------------------------------
/**Este metodo permirte hallar la solucion al problema
* cumpliendo con las condiciones establecidas,
* ademas en este metodo se calculara el tiempo que se demora en nanosegundos
 */
    public void hallarSolucion() {
        int i = 0;
        do {
            long tiempoInicio = System.nanoTime();
            System.out.println("Matriz # " + (i + 1));
            generarNumeros();
            llenarMatriz();
            imprimirMatriz();
            comprobarNumeritos();
            recorrerMatriz();
            getNums().clear();
            error = 0;
            long totalTiempo = System.nanoTime() - tiempoInicio;
            System.out.println("El tiempo de demora es :" + totalTiempo + " nanoseg");
            System.out.println();
            i++;
        } while (!isCorrecto());
        System.out.println("Resultado correcto en: " + i + " iteraciones");


    }
//    ---------------------------------------------------------------
//Este metodo genera los numeros aleatorios del 1 al 8
     public void generarNumeros() {
        int n = 0;
        for (int i = 0; i < 8; i++) {//for para colocar los ###
            n = (int) (Math.random() * 8 + 1);//genera el 5//6//5
            if (!nums.contains(n)) {
                nums.add(n);
            } else {
                i--;
            }
        }
    }
//Este metodo permite llenar la matriz en 3 filas y 4 columnas
//pero sin tomar en cuenta las esquinas de la matriz
    public void llenarMatriz() {
        int pos = 0;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 4; j++) {
                if ((i == 0 && j == 0) || (i == 0 && j == 3) || (i == 2 && j == 0) || (i == 2 && j == 3)) {
                    matriz[i][j] = 0;
                } else {
                    matriz[i][j] = nums.get(pos);
                    pos++;
                }
            }
        }

    }
//--------------------------------------------------
   /**Este metodo permite imprimir la matriz en 4 columnas y 3 filas
  */
    public void imprimirMatriz() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 4; j++) {
                if (matriz[i][j] == 0) {
                    System.out.print(" \t");
                } else {
                    System.out.print(matriz[i][j] + "\t");
                }
            }
            System.out.print("\n");
        }
    }
//--------------------------------------------------
   /**En este metodo se llama a los metodos que comprueban si los numeros
    * que se encuentran en las pocisiones [1,0], [1,1][1,2][1,3] son adyacentes
    * y de acuerdo a que devuelvan los metodos escribir si es correcto o no
  */
    public void comprobarNumeritos() {
        correcto = false;
        if (comprobarElemento10()) {
            if (comprobarElemento13()) {
                if (comprobarElemento11()) {
                    if (comprobarElemento12()) {
                        if (comprobarCentro()) {
                            System.out.println("Es correcto");
                            correcto = true;
                        } else {
                            System.out.println("No Es correcto");
                        }
                    } else {
                        System.out.println("No Es correcto");
                    }
                } else {
                    System.out.println("No Es correcto");
                }
            } else {
                System.out.println("No Es correcto");
            }
        } else {
            System.out.println("No Es correcto");
        }
    }
//con este metodo recorremos la matriz comprobando la adyacencia de los numeros
//        para demostrar que tiene errores o no, y con esto saber si es correcta
    public void recorrerMatriz() {
        error = 0;
        comprobarElemento10();
        comprobarElemento11();
        comprobarElemento12();
        comprobarElemento13();
        comprobarCentro();
        if (error == 0) {
            System.out.println("Matriz correcta");
        }
    }

    public void contarError() {
        error++;
    }

    public int numeroErrores() {
        return error;
    }

    public void llenarListaErrores() {
        errores.add(error);
    }
//----------------------------------------------------
//    se calcula la respuesta optima de acuerdo al numero de errores que tengan las respuestas
//    la matriz con menos errores es la optima
    public void calcularOptimo() {
        int numeroMenor = errores.get(0);
        int posicion = 0;
        for (int j = 1; j < errores.size(); j++) {
            if (errores.get(j) < numeroMenor) {
                numeroMenor = errores.get(j);
                posicion = j;
            }
        }
        System.out.println("La matriz # " + (posicion + 1) + " es la mas optima, tiene " + numeroMenor + " errores");
    }
//Con este metodo comprobamos,si los numeros adyacentes al numero que esta en la posicion 1,0
//    son consecutivos o no y nos devuelve un valor de true o false

    public boolean comprobarElemento10() {
        int num = matriz[1][0];
        boolean error = true;
        for (int i = 0; i < 3; i++) {
            if ((num + 1) == matriz[i][1] || (num - 1) == matriz[i][1]) {
                error = false;
                contarError();
            }
        }
        return error;
    }
//--------------------------------------------------------------------------------------------
//Con este metodo comprobamos,si los numeros adyacentes al numero que esta en la posicion 1,3
//son consecutivos o no para contar los errores y nos devuelve un valor de true o false
    public boolean comprobarElemento13() {
        int num = matriz[1][3];
        boolean error = true;
        for (int i = 0; i < 3; i++) {
            if ((num + 1) == matriz[i][2] || (num - 1) == matriz[i][2]) {
                error = false;
                contarError();
             }
        }
        return error;
    }
//--------------------------------------------------------------------------------------------
//Con este metodo comprobamos,si los numeros adyacentes al numero que esta en la posicion [1,1]
//son consecutivos o no para contar los errores y nos devuelve un valor de true o false

    public boolean comprobarElemento11() {
        int num = matriz[1][1];
        boolean error = true;
        for (int i = 0; i < 3; i++) {
            if ((num + 1) == matriz[i][1] || (num - 1) == matriz[i][1]) {
                error = false;
                contarError();
            }
            if ((num + 1) == matriz[i][2] || (num - 1) == matriz[i][2]) {
                if (i == 1) {
                } else {
                    error = false;
                    contarError();
                }
            }
        }
        return error;
    }
//--------------------------------------------------------------------------------------------
//Con este metodo comprobamos,si los numeros adyacentes al numero que esta en la posicion [1,2]
//son consecutivos o no para contar los errores y nos devuelve un valor de true o false

    public boolean comprobarElemento12() {
        int num = matriz[1][2];
        boolean error = true;
        for (int i = 0; i < 3; i++) {
            if ((num + 1) == matriz[i][1] || (num - 1) == matriz[i][1]) {
                error = false;
                contarError();
            }
            if ((num + 1) == matriz[i][2] || (num - 1) == matriz[i][2]) {
                error = false;
                contarError();
            }
        }
        return error;
    }
//--------------------------------------------------------------------------------------------
//Con este metodo comprobamos,si los numeros que estan en la posicion [0,1]y [0,2]son consecutivos
//y tambien si los numeros que estan en la posicion [2,1]y [2,2]son consecutivos

    public boolean comprobarCentro() {
        boolean error = true;
        if ((matriz[0][1] + 1 == matriz[0][2]) || (matriz[0][1] - 1 == matriz[0][2])) {
            error = false;
            contarError();
        }
        if ((matriz[2][1] + 1 == matriz[2][2]) || (matriz[2][1] - 1 == matriz[2][2])) {
            error = false;
            contarError();
        }
        return error;
    }

    public boolean isCorrecto() {
        return correcto;
    }

    public List<Integer> getNums() {
        return nums;
    }

    public List<Integer> getErrores() {
        return errores;
    }
//Con este metodo podemos imprimir los metodos a los cuales hagamos referencia,
//pueden ser los metodos para hallar la solucion, sin importar el numero de matrices que se generen
//o tambien para encontrar la solucion optima ingresando el nro de matrices que queremos generar
    public static void main(String[] args) {//factorial. 8! = 1*2*3*4*5*6*7*8 = 40320
        Puzzle p = new Puzzle();
        long tiempoInicio = System.currentTimeMillis();
        p.hallarOptimo(10);
//        p.hallarSolucion();
        long totalTiempo = System.currentTimeMillis() - tiempoInicio;
        System.out.println("El tiempo total de ejecucion es :" + totalTiempo + " miliseg");
    }
}
//           Soluciones
// ------------------------------
// 0        6        4        0 |
// 2        8        1        7 |
// 0        5        3        0 |
// ------------------------------
// 0        5        3        0 |
// 2        8        1        7 |
// 0        6        4        0 |
// ------------------------------
// 0        4        6        0 |
// 7        1        8        2 |
// 0        3        5        0 |
// ------------------------------
// 0        3        5        0 |
// 7        1        8        2 |
// 0        4        6        0 |
// ------------------------------